/* bzflag
 * Copyright (c) 1993-2020 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */


// interface header
#include "OccluderSceneNode.h"

// system headers
#include <math.h>
#include <string.h>
#include <glm/gtc/type_ptr.hpp>

// common implementation headers
#include "MeshFace.h"
#include "Intersect.h"
#include "ViewFrustum.h"


OccluderSceneNode::OccluderSceneNode(const MeshFace* face)
{
    int i;

    setOccluder(true);

    // record plane info
    plane = face->getPlane();

    // record extents info
    extents = face->getExtents();

    // record vertex info
    vertexCount = face->getVertexCount();
    vertices = new GLfloat3[vertexCount];
    for (i = 0; i < vertexCount; i++)
        memcpy(vertices[i], face->getVertex(i), sizeof(float[3]));

    // record sphere info
    auto mySphere = glm::vec4(0.0f);
    for (i = 0; i < vertexCount; i++)
    {
        const float* v = vertices[i];
        mySphere[0] += v[0];
        mySphere[1] += v[1];
        mySphere[2] += v[2];
    }
    mySphere[0] /= (float)vertexCount;
    mySphere[1] /= (float)vertexCount;
    mySphere[2] /= (float)vertexCount;

    for (i = 0; i < vertexCount; i++)
    {
        const float* v = vertices[i];
        const float dx = mySphere[0] - v[0];
        const float dy = mySphere[1] - v[1];
        const float dz = mySphere[2] - v[2];
        GLfloat r = ((dx * dx) + (dy * dy) + (dz * dz));
        if (r > mySphere[3])
            mySphere[3] = r;
    }
    setSphere(mySphere);

    return;
}


OccluderSceneNode::~OccluderSceneNode()
{
    delete[] vertices;
    return;
}


const glm::vec4 OccluderSceneNode::getPlane() const
{
    return plane;
}

bool OccluderSceneNode::cull(const ViewFrustum& frustum) const
{
    // cull if eye is behind (or on) plane
    const auto eye = frustum.getEye();
    if (((eye[0] * plane[0]) + (eye[1] * plane[1]) + (eye[2] * plane[2]) +
            plane[3]) <= 0.0f)
        return true;

    // if the Visibility culler tells us that we're
    // fully visible, then skip the rest of these tests
    if (octreeState == OctreeVisible)
        return false;

    const Frustum* f = (const Frustum *) &frustum;
    if (testAxisBoxInFrustum(extents, f) == Outside)
        return true;

    // probably visible
    return false;
}


bool OccluderSceneNode::inAxisBox (const Extents& exts) const
{
    if (!extents.touches(exts))
        return false;

    return testPolygonInAxisBox (vertexCount, vertices, plane, exts);
}


const glm::vec3 OccluderSceneNode::getVertex (int vertex) const
{
    return glm::make_vec3(vertices[vertex]);
}

// Local Variables: ***
// mode: C++ ***
// tab-width: 4 ***
// c-basic-offset: 4 ***
// indent-tabs-mode: nil ***
// End: ***
// ex: shiftwidth=4 tabstop=4
